package com.ajguan.library;


import com.ajguan.library.exception.ERVHRuntimeException;
import com.ajguan.library.view.SimpleLoadMoreView;
import com.ajguan.library.view.SimpleRefreshHeaderView;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.components.*;
import ohos.app.Context;
import ohos.eventhandler.EventHandler;
import ohos.eventhandler.EventRunner;
import ohos.multimodalinput.event.TouchEvent;

import java.math.BigDecimal;


/**
 * Created by guanaj on 16/9/2.
 */
public class EasyRefreshLayout extends ComponentContainer implements Component.EstimateSizeListener, Component.TouchEventListener, ComponentContainer.ArrangeListener {
    private static final String TAG = EasyRefreshLayout.class.getSimpleName();
    private int SCROLL_TO_REFRESH_DURATION = 250;
    private int SCROLL_TO_TOP_DURATION = 500;
    private static final int INVALID_POINTER = -1;
    private static final float DRAG_RATE = 1.0f;
    private static final int START_POSITION = 0;
    private long SHOW_COMPLETED_TIME = 500;
    private long SCROLL_TO_LOADING_DURATION = 500;
    private static final long SHOW_SCROLL_DOWN_DURATION = 300;
    private static final int HORIZONTAL_STATE = 1;
    private static final int VERTICAL_STATE = 2;
    private static final int NONE_STATE = 0;
    private double pull_resistance = 2.0f;

    private State state = State.RESET;
    // private Logger logger = Logger.getLogger(TAG);
    private boolean isEnablePullToRefresh = true;
    private boolean isRefreshing;


    private int touchSlop;
    private Component refreshHeaderView;
    private int currentOffsetTop;

    private Component contentView;
    private boolean hasMeasureHeaderView = false;
    private int headerViewHight;
    private int totalDragDistance;
    private int activePointerId;
    private boolean isTouch;
    private boolean hasSendCancelEvent;
    private boolean isBeginDragged;
    private int lastOffsetTop;
    private float lastMotionX;
    private float lastMotionY;
    private float initDownY;
    private float initDownX;
    private TouchEvent lastEvent;
    private AutoScroll autoScroll;
    private boolean isAutoRefresh;
    private EasyEvent easyEvent;


    private ListContainer mRecyclerView;
    boolean isCanLoad = false;
    private boolean isLoading = false;
    private Component mLoadMoreView;
    private boolean isLoadingFail = false;
    private float offsetY;
    private float mDistance;
    //开启预加载标识位
    //1、表示不开启
    //2、表示开启普通加载
    //3、表示开启预加载模式
    private LoadModel loadMoreModel = LoadModel.COMMON_MODEL;
    //还剩多少个item时触发预加载
    private int advanceCount = 0;
    /**
     * run task event
     */
    EventHandler eventHandler = new EventHandler(EventRunner.getMainEventRunner());

    /**
     * 滑动状态
     */
    private int moveState = NONE_STATE;

    public EasyRefreshLayout(Context context) {
        this(context, null);
    }

    public EasyRefreshLayout(Context context, AttrSet attrs) {
        this(context, attrs, null);
    }

    public EasyRefreshLayout(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        initParameter(context, attrSet);
    }

    /**
     * 延迟几秒后恢复原状
     */
    private final Runnable delayToScrollTopRunnable = new Runnable() {
        @Override
        public void run() {
            autoScroll.scrollTo(START_POSITION, SCROLL_TO_TOP_DURATION);
        }
    };
    /**
     * 自动变换到刷新状态
     */
    private final Runnable autoRefreshRunnable = new Runnable() {
        @Override
        public void run() {
            isAutoRefresh = true;
            changeState(State.PULL);
            autoScroll.scrollTo(totalDragDistance, SCROLL_TO_REFRESH_DURATION);
        }
    };

    private boolean isRecycerView;
    private boolean isNotMoreLoading;


    private void initParameter(Context context, AttrSet attrs) {
        //todo
        touchSlop = 8;
        /*init an refreshHeadView*/
        Component refreshHeadView = getDefaultRefreshView();
        setRefreshHeadView(refreshHeadView);

        Component loadMoreView = getDefaultLoadMoreView();
        setLoadMoreView(loadMoreView);
        autoScroll = new AutoScroll();
        setEstimateSizeListener(this);
        setArrangeListener(this);
        setTouchEventListener(this);
    }

    // 设置头部视图
    public void setRefreshHeadView(Component headerView) {
        if (headerView == null)
            return;
        if (refreshHeaderView != null && headerView != refreshHeaderView)
            removeComponent(refreshHeaderView);

        /*设置默认的布局参数*/
        LayoutConfig layoutParams = headerView.getLayoutConfig();
        if (layoutParams == null) {
            layoutParams = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_CONTENT);
            headerView.setLayoutConfig(layoutParams);
        }

        /*设置为新的headerView*/
        refreshHeaderView = headerView;
        addComponent(refreshHeaderView);
    }


    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        int widthSize = EstimateSpec.getSize(widthMeasureSpec);
        int heightSize = EstimateSpec.getSize(heightMeasureSpec);
        setEstimatedSize(widthSize, heightSize);

        if (contentView == null) {
            initContentView();
        }
        // 没有contentView
        if (contentView == null)
            return true;

        /*测量填充主要内容的View*/
        /*让contentView占满整个屏幕*/
        int contentViewWidth = getEstimatedWidth() - getPaddingLeft() - getPaddingRight();
        int contentViewHeight = getEstimatedHeight() - getPaddingTop() - getPaddingBottom();

        int sizeWithMode = EstimateSpec.getSizeWithMode(contentViewWidth, EstimateSpec.PRECISE);
        int sizeHeightMode = EstimateSpec.getSizeWithMode(contentViewHeight, EstimateSpec.PRECISE);
        contentView.estimateSize(sizeWithMode, sizeHeightMode);

        //todo
        /*测量headerView*/
        measureChild(refreshHeaderView, widthMeasureSpec, heightMeasureSpec);
        if (!hasMeasureHeaderView) {
            /*headerView还没有被测量*/
            hasMeasureHeaderView = true;
            //获取测量的高度
            headerViewHight = refreshHeaderView.getEstimatedHeight();
            totalDragDistance = headerViewHight;
        }

        /*测量loadMoreView*/
        measureChild(mLoadMoreView, widthMeasureSpec, heightMeasureSpec);
        return true;
    }


    /**
     * Ask one of the children of this view to measure itself, taking into
     * account both the MeasureSpec requirements for this view and its padding.
     * The heavy lifting is done in getChildMeasureSpec.
     *
     * @param child                   The child to measure
     * @param parentWidthMeasureSpec  The width requirements for this view
     * @param parentHeightMeasureSpec The height requirements for this view
     */
    protected void measureChild(Component child, int parentWidthMeasureSpec,
                                int parentHeightMeasureSpec) {
        final LayoutConfig lp = child.getLayoutConfig();

        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
                getPaddingLeft() + getPaddingRight(), lp.width);
        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
                getPaddingTop() + getPaddingBottom(), lp.height);

        child.estimateSize(childWidthMeasureSpec, childHeightMeasureSpec);
    }


    /**
     * Does the hard part of measureChildren: figuring out the MeasureSpec to
     * pass to a particular child. This method figures out the right MeasureSpec
     * for one dimension (height or width) of one child view.
     * <p>
     * The goal is to combine information from our MeasureSpec with the
     * LayoutParams of the child to get the best possible results. For example,
     * if the this view knows its size (because its MeasureSpec has a mode of
     * EXACTLY), and the child has indicated in its LayoutParams that it wants
     * to be the same size as the parent, the parent should ask the child to
     * layout given an exact size.
     *
     * @param spec           The requirements for this view
     * @param padding        The padding of this view for the current dimension and
     *                       margins, if applicable
     * @param childDimension How big the child wants to be in the current
     *                       dimension
     * @return a MeasureSpec integer for the child
     */
    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
        int specMode = EstimateSpec.getMode(spec);
        int specSize = EstimateSpec.getSize(spec);

        int size = Math.max(0, specSize - padding);

        int resultSize = 0;
        int resultMode = 0;

        switch (specMode) {
            // Parent has imposed an exact size on us
            case EstimateSpec.PRECISE:
                if (childDimension >= 0) {
                    resultSize = childDimension;
                    resultMode = EstimateSpec.PRECISE;
                } else if (childDimension == LayoutConfig.MATCH_PARENT) {
                    // Child wants to be our size. So be it.
                    resultSize = size;
                    resultMode = EstimateSpec.PRECISE;
                } else if (childDimension == LayoutConfig.MATCH_CONTENT) {
                    // Child wants to determine its own size. It can't be
                    // bigger than us.
                    resultSize = size;
                    resultMode = EstimateSpec.NOT_EXCEED;
                }
                break;

            // Parent has imposed a maximum size on us
            case EstimateSpec.NOT_EXCEED:
                if (childDimension >= 0) {
                    // Child wants a specific size... so be it
                    resultSize = childDimension;
                    resultMode = EstimateSpec.PRECISE;
                } else if (childDimension == LayoutConfig.MATCH_PARENT) {
                    // Child wants to be our size, but our size is not fixed.
                    // Constrain child to not be bigger than us.
                    resultSize = size;
                    resultMode = EstimateSpec.NOT_EXCEED;
                } else if (childDimension == LayoutConfig.MATCH_CONTENT) {
                    // Child wants to determine its own size. It can't be
                    // bigger than us.
                    resultSize = size;
                    resultMode = EstimateSpec.NOT_EXCEED;
                }
                break;
            // Parent asked to see how big we want to be
            case EstimateSpec.UNCONSTRAINT:
                if (childDimension >= 0) {
                    // Child wants a specific size... let him have it
                    resultSize = childDimension;
                    resultMode = EstimateSpec.PRECISE;
                } else if (childDimension == LayoutConfig.MATCH_PARENT) {
                    // Child wants to be our size... find out how big it should
                    // be
                    resultSize = 0;
                    resultMode = EstimateSpec.UNCONSTRAINT;
                } else if (childDimension == LayoutConfig.MATCH_CONTENT) {
                    // Child wants to determine its own size.... find out how
                    // big it should be
                    resultSize = 0;
                    resultMode = EstimateSpec.UNCONSTRAINT;
                }
                break;
        }
        // noinspection ResourceType
        return EstimateSpec.getSizeWithMode(resultSize, resultMode);
    }


    private void initContentView() {
        // Don't bother getting the parent height if the parent hasn't been laid
        // out yet.
        if (contentView == null) {
            for (int i = 0; i < getChildCount(); i++) {
                Component child = getComponentAt(i);
                if (!child.equals(refreshHeaderView) && !child.equals(mLoadMoreView)) {
                    contentView = child;
                    if (contentView instanceof ListContainer) {
                        isRecycerView = true;
                    } else {
                        isRecycerView = false;
                    }
                    break;
                }
            }
        }
        if (isRecycerView)
            initERVH();
    }

    @Override
    public boolean onArrange(int i, int i1, int i2, int i3) {
        final int width = getEstimatedWidth();
        final int height = getEstimatedHeight();
        if (getChildCount() == 0) {
            /*不存在子View*/
            ELog.e(TAG, "child view can not be empty");
            return false;
        }

        if (contentView == null) {
            initContentView();
        }
        if (contentView == null) {
            ELog.e(TAG, "main content of view can not be empty ");
            return false;
        }

        final Component child = contentView;
        final int childLeft = getPaddingLeft();
        final int childTop = getPaddingTop() + currentOffsetTop;
        final int childWidth = width - getPaddingLeft() - getPaddingRight();
        final int childHeight = height - getPaddingTop() - getPaddingBottom();
        /*设置contentView 的位置*/
        child.arrange(childLeft, childTop, childWidth, childHeight);
        // header放到contentView的上方，水平居中
        int refreshViewWidth = refreshHeaderView.getEstimatedWidth();
        refreshHeaderView.arrange((width / 2 - refreshViewWidth / 2),
                -headerViewHight + currentOffsetTop,
                (refreshViewWidth),
                headerViewHight);


        //loadMoreView 放到contentView 下方

        int loadMoreViewWidth = mLoadMoreView.getEstimatedWidth();
        int loadMoreViewHeight = mLoadMoreView.getEstimatedWidth();
        int loadL = width / 2 - loadMoreViewWidth / 2;
        mLoadMoreView.arrange(loadL, childHeight, loadMoreViewWidth, loadMoreViewHeight);
        return true;
    }


    /*处理触摸事件*/
    @Override
    public boolean onTouchEvent(Component component, TouchEvent ev) {

        if (isLoading || contentView == null) {
            return false;
        }

        //获取支持多点触控的action
        final int actionMasked = ev.getAction();
        switch (actionMasked) {
            case TouchEvent.PRIMARY_POINT_DOWN: {
                mDistance = 0;
                // logger.info("ACTION_DOWN");
                //获得首次按下的触摸事件的id
                activePointerId = ev.getPointerId(0);
                isTouch = true;
                hasSendCancelEvent = false;
                // 是否开始下拉
                isBeginDragged = false;
                // 上一次contentView的偏移高度
                lastOffsetTop = currentOffsetTop;
                currentOffsetTop = contentView.getTop();
                // 手指按下时的坐标
                initDownX = lastMotionX = ev.getPointerScreenPosition(0).getX();
                initDownY = lastMotionY = ev.getPointerScreenPosition(0).getY();
                autoScroll.stop();
                removeCallbacks(delayToScrollTopRunnable);
                removeCallbacks(autoRefreshRunnable);
                //表示消耗了该事件,以便子view都没有消耗而导致后面接收不到该事件的事件序列
                moveState = NONE_STATE;
                return true;
            }

            case TouchEvent.POINT_MOVE: {
                if (activePointerId == INVALID_POINTER) {
                    // Log.e(TAG, "Got ACTION_MOVE event but don't have an active pointer id.");
                    return false;
                }
                autoScroll.stop();

                // 最后一次move事件
                lastEvent = ev;
                float xPoint = ev.getPointerScreenPosition(ev.getIndex()).getX();
                float yPoint = ev.getPointerScreenPosition(ev.getIndex()).getY();
                float xDiff = BigDecimal.valueOf(xPoint)
                        .subtract(BigDecimal.valueOf(lastMotionX))
                        .floatValue();
                float yDiff = BigDecimal.valueOf(yPoint)
                        .subtract(BigDecimal.valueOf(lastMotionY))
                        .floatValue();
                float absY = BigDecimal.valueOf(yPoint)
                        .subtract(BigDecimal.valueOf(initDownY))
                        .abs()
                        .floatValue();
                float absX = BigDecimal.valueOf(xPoint)
                        .subtract(BigDecimal.valueOf(initDownX))
                        .abs()
                        .floatValue();
                lastMotionX = xPoint;
                lastMotionY = yPoint;
                if (moveState == NONE_STATE && (absX > absY || Math.abs(xDiff) > Math.abs(yDiff))/*&& isInterceptMoveEvent*/) {
                    /*左右滑动了*/
                    // logger.info("左右滑动了");
                    moveState = HORIZONTAL_STATE;
                    break;
                } else if (moveState == NONE_STATE && (absX < absY || Math.abs(xDiff) < Math.abs(yDiff))) {
                    moveState = VERTICAL_STATE;
                    // logger.info("上下滑动");
                }
                // logger.info(moveState + "");
                if (moveState == HORIZONTAL_STATE)
                    break;

                if (moveState != VERTICAL_STATE)
                    break;


                mDistance = BigDecimal.valueOf(mDistance)
                        .add(BigDecimal.valueOf(yDiff))
                        .floatValue();
                offsetY = BigDecimal.valueOf(yDiff)
                        .multiply(BigDecimal.valueOf(DRAG_RATE))
                        .floatValue();


                float abs = BigDecimal.valueOf(yPoint)
                        .subtract(BigDecimal.valueOf(initDownY))
                        .abs()
                        .floatValue();
                if (!isBeginDragged && abs > touchSlop) {
                    isBeginDragged = true;
                }
                if (isBeginDragged) {
                    boolean isMoveHeadDown = offsetY > 0; // ↓
                    boolean canMoveHeadDown = !canChildScrollUp();
                    boolean isMoveHeadUp = !isMoveHeadDown;     // ↑
                    boolean canMoveHeadUp = currentOffsetTop > START_POSITION;

                    // 判断是否拦截事件
                    if ((isMoveHeadDown && canMoveHeadDown) || (isMoveHeadUp && canMoveHeadUp)) {
                        moveSpinner(offsetY);
                        return true;
                    }
                }
                break;
            }

            case TouchEvent.CANCEL:
                // logger.info("ACTION_CANCEL");
            case TouchEvent.PRIMARY_POINT_UP: {
                // logger.info("ACTION_UP");
                if (currentOffsetTop > START_POSITION) {
                    finishSpinner();
                }
                isTouch = false;
                moveState = NONE_STATE;
                activePointerId = INVALID_POINTER;
                break;
            }
            case TouchEvent.OTHER_POINT_DOWN: {
                // logger.info( "ACTION_POINTER_DOWN");
                int pointerIndex = ev.getIndex();
                if (pointerIndex < 0) {
                    //   Log.e(TAG, "Got ACTION_POINTER_DOWN event but have an invalid action index.");
                    return false;
                }
                lastMotionX = ev.getPointerScreenPosition(pointerIndex).getX();
                lastMotionY = ev.getPointerScreenPosition(pointerIndex).getY();
                activePointerId = ev.getPointerId(pointerIndex);
                break;
            }
            case TouchEvent.OTHER_POINT_UP: {
                // logger.info("ACTION_POINTER_UP");
                onSecondaryPointerUp(ev);
                lastMotionY = ev.getPointerScreenPosition(ev.getIndex()).getY();
                lastMotionX = ev.getPointerScreenPosition(ev.getIndex()).getX();
                break;
            }
        }
        return isTouch;
    }


    private void onSecondaryPointerUp(TouchEvent ev) {
        /*目前只支持两点触控*/
        final int pointerIndex = ev.getIndex();
        // Log.i(TAG, "pointerIndex:" + pointerIndex);
        final int pointerId = ev.getPointerId(pointerIndex);
        if (pointerId == activePointerId) {
            // This was our active pointer going up. Choose a new
            // active pointer and adjust accordingly.
            final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
            lastMotionY = ev.getPointerScreenPosition(newPointerIndex).getY();
            lastMotionX = ev.getPointerScreenPosition(newPointerIndex).getX();
            activePointerId = ev.getPointerId(newPointerIndex);
        }
    }

    private void moveSpinner(float offsetY) {
        if (!isEnablePullToRefresh) {
            return;
        }
        int offset = Math.round(offsetY);
        if (offset == 0) {
            return;
        }

        // 发送cancel事件给child
        if (!hasSendCancelEvent && isTouch && currentOffsetTop > START_POSITION) {
            sendCancelEvent();
            hasSendCancelEvent = true;
        }
        int nextOffsetTop = Math.max(0, currentOffsetTop + offset); // contentView不能移动到小于0的位置……
        offset = nextOffsetTop - currentOffsetTop;

        // y = x - (x/2)^2
        int extraOS = nextOffsetTop - totalDragDistance;
        double slingshotDist = totalDragDistance;
        double tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2) / slingshotDist);
        double tensionPercent = (tensionSlingshotPercent - Math.pow(tensionSlingshotPercent / this.pull_resistance, 2));

        if (offset > 0) { // 下拉的时候才添加阻力
            offset = (int) (offset * (1f - tensionPercent));
            nextOffsetTop = Math.max(0, currentOffsetTop + offset);
        }
        // 1. 在RESET状态时，第一次下拉出现header的时候，设置状态变成PULL
        if (state == State.RESET && currentOffsetTop == START_POSITION && nextOffsetTop > 0) {
            if (isNotMoreLoading || isLoadingFail)
                closeLoadView();
            changeState(State.PULL);
        }

        // 2. 在PULL或者COMPLETE状态时，header回到顶部的时候，状态变回RESET


        if (currentOffsetTop > START_POSITION && nextOffsetTop <= START_POSITION) {
            //  Log.i(TAG, "2--contentViewY:" + nextOffsetTop + "--START_POSITION:" + START_POSITION);

            if (state == State.PULL || state == State.COMPLETE) {
                changeState(State.RESET);
            }
        }

        // 3. 如果是从底部回到顶部的过程(往上滚动)，并且手指是松开状态, 并且当前是PULL状态，状态变成LOADING，这时候我们需要强制停止autoScroll

        if (state == State.PULL && !isTouch && currentOffsetTop > totalDragDistance && nextOffsetTop <= totalDragDistance) {
            //  Log.i(TAG, "3--contentViewY:" + nextOffsetTop + "--totalDragDistance:" + totalDragDistance);

            autoScroll.stop();
            changeState(State.REFRESHING);
            if (easyEvent != null) {
                isRefreshing = true;
                easyEvent.onRefreshing();
            }
            // 因为判断条件targetY <= totalDragDistance，会导致不能回到正确的刷新高度（有那么一丁点偏差），调整change
            int adjustOffset = totalDragDistance - nextOffsetTop;
            offset += adjustOffset;
        }

        setTargetOffsetTopAndBottom(offset);
        // 别忘了回调header的位置改变方法。
        if (refreshHeaderView instanceof IRefreshHeader) {
            ((IRefreshHeader) refreshHeaderView)
                    .onPositionChange(currentOffsetTop, lastOffsetTop, totalDragDistance, isTouch, state);
        }


    }

    private void finishSpinner() {
        if (state == State.REFRESHING) {
            if (currentOffsetTop > totalDragDistance) {
                autoScroll.scrollTo(totalDragDistance, SCROLL_TO_REFRESH_DURATION);
            }
        } else {
            autoScroll.scrollTo(START_POSITION, SCROLL_TO_TOP_DURATION);
        }
    }

    /**
     * 判读是否可以向上滑动
     * {@link Component#DRAG_UP}   Indicates that the scroll bar is dragged upwards.
     * {@link Component#DRAG_DOWN} Indicates the scroll bar is dragged downwards.
     *
     * @return child 是否可以向上滑动
     */
    private boolean canChildScrollUp() {
        boolean canScroll = contentView.canScroll(DRAG_DOWN);
//        ELog.i(TAG, "canScroll:" + canScroll);
        /*return true can  swipe up*/
        return canScroll;

    }

    /**
     * 滚动距离
     *
     * @param offset 滚动偏移量
     */
    private void setTargetOffsetTopAndBottom(int offset) {
        if (offset == 0) {
            return;
        }
        offsetTopAndBottom(contentView, offset);
        offsetTopAndBottom(refreshHeaderView, offset);
        lastOffsetTop = currentOffsetTop;
        currentOffsetTop = contentView.getTop();
        invalidate();
    }

    /**
     * 设置顶部与底部的偏移量
     *
     * @param target 偏移target
     * @param offset 偏移量
     */
    private void offsetTopAndBottom(Component target, int offset) {
        target.setTop(target.getTop() + offset);
        target.setBottom(target.getBottom() + offset);
        target.setComponentPosition(target.getLeft(), target.getTop(), target.getRight(), target.getBottom());
    }

    /**
     * ACTION——MOVE事件已被处理,子View不再接收该事件序列
     * 直到下一次DOWN事件触发
     */
    private void sendCancelEvent() {
        if (lastEvent == null) {
            return;
        }
        //todo
        ELog.w(TAG, "start sendCancelEvent");
//        TouchEvent ev = TouchEvent.obtain(lastEvent);
//        ev.setAction(TouchEvent.CANCEL);


    }

    private void changeState(State state) {
        // Toast.makeText(getContext(), state.toString(), Toast.LENGTH_SHORT).show();

        this.state = state;

        IRefreshHeader refreshHeader = this.refreshHeaderView instanceof IRefreshHeader ? ((IRefreshHeader) this.refreshHeaderView) : null;
        if (refreshHeader != null) {
            switch (state) {
                case RESET:
                    refreshHeader.reset();
                    break;
                case PULL:
                    refreshHeader.pull();
                    break;
                case REFRESHING:
                    refreshHeader.refreshing();
                    break;
                case COMPLETE:
                    refreshHeader.complete();
                    break;
            }
        }
    }


    /**
     * 刷新完成
     */
    public void refreshComplete() {
        isRefreshing = false;
        changeState(State.COMPLETE);
        // if refresh completed and the contentView at top, change state to reset.
        if (currentOffsetTop == START_POSITION) {
            changeState(State.RESET);
        } else {
            // waiting for a time to show refreshView completed state.
            // at next touch event, remove this runnable
            if (!isTouch) {
                postDelayed(delayToScrollTopRunnable, SHOW_COMPLETED_TIME);
            }
        }
    }

    /**
     * remove runnable
     *
     * @param runnable run task
     */
    private void removeCallbacks(Runnable runnable) {
        if (eventHandler != null)
            eventHandler.removeTask(runnable);
    }


    private void postDelayed(Runnable runnable, long delay) {
        if (eventHandler != null)
            eventHandler.postTask(runnable, delay);
    }

    public void autoRefresh() {
        autoRefresh(500);
    }

    /**
     * 在onCreate中调用autoRefresh，此时View可能还没有初始化好，需要延长一段时间执行。
     *
     * @param duration 延时执行的毫秒值
     */
    public void autoRefresh(long duration) {
        if (state != State.RESET) {
            return;
        }
        postDelayed(autoRefreshRunnable, duration);
    }


    public Component getDefaultRefreshView() {
        SimpleRefreshHeaderView refreshHeaderView = new SimpleRefreshHeaderView(getContext());
        LayoutConfig layoutParams = new LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_CONTENT);
        refreshHeaderView.setLayoutConfig(layoutParams);
        return refreshHeaderView;
    }

    //Checks whether the content has scrolled to a valid position.
    private class AutoScroll {
        private final ValueAnimator animation;

        public AutoScroll() {
            animation = ValueAnimator.ofObject(contentView,
                    0,
                    -AttrHelper.vp2px(60, getContext()),
                    ValueAnimator.Property.TRANSLATION_Y

            );
            animation.setRepeatCount(0);
            animation.setInterpolatorType(AnimatorValue.CurveType.DECELERATE);
            animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(AnimatorValue animator, float fraction, Object animatedValue) {

                    final float currY = (float) animatedValue;
                    float offset = BigDecimal.valueOf(currY).subtract(BigDecimal.valueOf(currentOffsetTop)).floatValue();
                    /*使用当前线程运行*/
                    moveSpinner(offset); // 调用此方法移动header和contentView
                    onScrollFinish(false);

                }
            });
            animation.addListener(new ValueAnimator.AnimatorListener() {
                @Override
                public void onAnimationStart(ValueAnimator animation) {

                }

                @Override
                public void onAnimationEnd(ValueAnimator animation) {

                }

                @Override
                public void onAnimationStop(ValueAnimator animation) {
                    stop();
                    onScrollFinish(true);
                }

                @Override
                public void onAnimationCancel(ValueAnimator animation) {
                    stop();
                    onScrollFinish(true);
                }

                @Override
                public void onAnimationRepeat(ValueAnimator animation) {

                }

                @Override
                public void onAnimationPause(ValueAnimator animation) {

                }

                @Override
                public void onAnimationResume(ValueAnimator animation) {

                }
            });
        }


        public void scrollTo(final int to, int duration) {
            final int from = currentOffsetTop;
            int distance = to - from;
            stop();
            if (distance == 0) {
                return;
            }
            //滑动到原位
//            scroller.startScroll(0, 0, 0, distance, duration);
            animation.setDuration(duration);
            animation.setIntValues(from, to);
            animation.start();
        }


        private void stop() {
            if (animation != null) {
                animation.stop();
            }
        }
    }


    /**
     * 在scroll结束的时候会回调这个方法
     *
     * @param isForceFinish 是否是强制结束的
     */

    private void onScrollFinish(boolean isForceFinish) {
        if (isAutoRefresh && !isForceFinish) {
            isAutoRefresh = false;
            changeState(State.REFRESHING);
            if (easyEvent != null) {
                easyEvent.onRefreshing();
            }
            finishSpinner();
        }
    }

    // 定义一个侦听器
    public interface OnRefreshListener {
        void onRefreshing();
    }

    public interface EasyEvent extends OnRefreshListener, LoadMoreEvent {

    }

    // 提供外部设置方法
    public void addEasyEvent(EasyEvent event) {
        if (event == null) {
            throw new ERVHRuntimeException("adapter can not be null");
        }
        this.easyEvent = event;
    }

    public boolean isEnablePullToRefresh() {
        return isEnablePullToRefresh;
    }

    /**
     * 是否下拉刷新
     *
     * @param enable true表示可以 false表示不可以
     */
    public void setEnablePullToRefresh(boolean enable) {
        isEnablePullToRefresh = enable;
    }

    public boolean isRefreshing() {
        return isRefreshing;

    }

    public void setRefreshing(boolean refreshing) {
        if (refreshing) {
            changeState(State.REFRESHING);
            if (isNotMoreLoading || isLoadingFail) {
                closeLoadView();
            }
        }
        changeState(State.RESET);
    }


    private void initERVH() {
        if (mLoadMoreView == null) {
            getDefaultLoadMoreView();
            setLoadMoreView(mLoadMoreView);
        }
        if (!isRecycerView) {
            return;
        }
        mRecyclerView = (ListContainer) contentView;

        mRecyclerView.addScrolledListener(new ScrolledListener() {
            @Override
            public void scrolledStageUpdate(Component component, int newStage) {
                ELog.i(TAG, "scrolledStageUpdate:" + newStage);
                if (loadMoreModel == LoadModel.ADVANCE_MODEL
                        && newStage != Component.SCROLL_AUTO_STAGE) {
                    checkAdvance();
                    return;
                }
                if (Math.abs(mDistance) > touchSlop && mDistance < 0) {
                    if (!isLoading && loadMoreModel == LoadModel.COMMON_MODEL && !isRefreshing && !isLoadingFail && !isNotMoreLoading) {
                        final int lastVisibleItem = getLastVisiBleItem();
                        int totalItemCount = mRecyclerView.getItemProvider().getCount();
                        int totalChildCount = mRecyclerView.getChildCount();
                        if (totalChildCount > 0 && lastVisibleItem >= totalItemCount - 1 && totalItemCount >= totalChildCount) {
                            isCanLoad = true;
                        }
                        if (isCanLoad) {
                            isCanLoad = false;
                            isLoading = true;
                            ((ILoadMoreView) mLoadMoreView).reset();

                            mLoadMoreView.estimateSize(0, 0);
                            ((ILoadMoreView) mLoadMoreView).loading();
                            showLoadView();
                        }
                    }
                }
            }

            @Override
            public void onContentScrolled(Component component, int i, int i1, int i2, int i3) {
                if (loadMoreModel == LoadModel.ADVANCE_MODEL)
                    checkAdvance();
            }
        });

    }

    /**
     * 加载更多
     */
    private void checkAdvance() {
        if (!isLoading && !isRefreshing && !isLoadingFail && !isNotMoreLoading) {
            final int lastVisibleItem = getLastVisiBleItem();
            int totalItemCount = mRecyclerView.getItemProvider().getCount();
            int totalChildCount = mRecyclerView.getChildCount();
            if (totalChildCount > 0 && lastVisibleItem >= totalItemCount - 1 - advanceCount && totalItemCount >= totalChildCount) {
                isCanLoad = true;
            }
            if (isCanLoad) {
                isCanLoad = false;
                isLoading = true;
                if (easyEvent != null) {
                    easyEvent.onLoadMore();
                }

            }
        }


    }

    private void showLoadView() {

        ValueAnimator animator = ValueAnimator.ofObject(
                mLoadMoreView,
                0,
                -mLoadMoreView.getEstimatedHeight(),
                ValueAnimator.Property.TRANSLATION_Y
        );
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

            @Override
            public void onAnimationUpdate(AnimatorValue animation, float fraction, Object animatedValue) {
                float ds = animator.getAnimatedValue();
                if (mLoadMoreView.getComponentParent() != null) {
                    ComponentContainer parent = (ComponentContainer) mLoadMoreView.getComponentParent();
                    parent.moveChildToFront(mLoadMoreView);
                }
                mLoadMoreView.setTranslationY(ds);
            }
        });
        animator.addListener(new ValueAnimator.AnimatorListener() {
            @Override
            public void onAnimationStart(ValueAnimator animation) {

            }

            @Override
            public void onAnimationEnd(ValueAnimator animation) {
                if (easyEvent != null) {
                    easyEvent.onLoadMore();
                }
            }

            @Override
            public void onAnimationStop(ValueAnimator animation) {

            }

            @Override
            public void onAnimationCancel(ValueAnimator animation) {

            }

            @Override
            public void onAnimationRepeat(ValueAnimator animation) {

            }

            @Override
            public void onAnimationPause(ValueAnimator animation) {

            }

            @Override
            public void onAnimationResume(ValueAnimator animation) {

            }
        });

        animator.setDuration(SCROLL_TO_LOADING_DURATION);
        animator.start();
    }

    private void hideLoadView() {
        // setTargetOffsetTopAndBottom( mLoadMoreView.getMeasuredHeight());
        if (mLoadMoreView != null && isRecycerView) {
            ValueAnimator animation = ValueAnimator.ofObject(mLoadMoreView,
                    0,
                    mLoadMoreView.getEstimatedHeight(),
                    ValueAnimator.Property.TRANSLATION_Y

            );
            animation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

                @Override
                public void onAnimationUpdate(AnimatorValue animator, float fraction, Object animatedValue) {
                    float ds = animation.getAnimatedValue();
                    if (mLoadMoreView.getComponentParent() != null) {
                        ComponentContainer componentParent = (ComponentContainer) mLoadMoreView.getComponentParent();
                        componentParent.moveChildToFront(mLoadMoreView);
                    }
                    mLoadMoreView.setTranslationY(ds);

                }

            });
            animation.addListener(new ValueAnimator.AnimatorListener() {

                @Override
                public void onAnimationStart(ValueAnimator animation) {

                }

                @Override
                public void onAnimationEnd(ValueAnimator animation) {
                    isLoading = false;
                }

                @Override
                public void onAnimationStop(ValueAnimator animation) {

                }

                @Override
                public void onAnimationCancel(ValueAnimator animation) {
                    isLoading = false;

                }

                @Override
                public void onAnimationRepeat(ValueAnimator animation) {
                    isLoading = false;

                }

                @Override
                public void onAnimationPause(ValueAnimator animation) {

                }

                @Override
                public void onAnimationResume(ValueAnimator animation) {

                }
            });
            animation.setDuration(SHOW_SCROLL_DOWN_DURATION);
            animation.start();
        }

    }

    public void closeLoadView() {
        if (loadMoreModel == LoadModel.ADVANCE_MODEL) {
            throw new RuntimeException("enableAdance Model cant not called closeLoadView method");

        } else {
            // setTargetOffsetTopAndBottom( mLoadMoreView.getMeasuredHeight());
            if (mLoadMoreView != null && isRecycerView) {
                // setTargetOffsetTopAndBottom(mLoadMoreView.getMeasuredHeight());

                if (mLoadMoreView.getComponentParent() != null) {
                    ComponentContainer componentParent = (ComponentContainer) mLoadMoreView.getComponentParent();
                    componentParent.moveChildToFront(mLoadMoreView);
                }
                mLoadMoreView.setTranslationY(mLoadMoreView.getEstimatedHeight());
                resetLoadMoreState();

            }
        }

    }


    public Component getLoadMoreView() {
        return (Component) getDefaultLoadMoreView();
    }

    public void setLoadMoreView(Component loadMoreView) {
        if (loadMoreView == null)
            throw new ERVHRuntimeException("loadMoreView can not be null");
        if (mLoadMoreView != null && loadMoreView != mLoadMoreView)
            removeComponent(mLoadMoreView);

        /*设置默认的布局参数*/
        LayoutConfig layoutParams = loadMoreView.getLayoutConfig();
        if (layoutParams == null) {
            layoutParams = new DirectionalLayout.LayoutConfig(LayoutConfig.MATCH_PARENT, LayoutConfig.MATCH_CONTENT);
            loadMoreView.setLayoutConfig(layoutParams);
        }
        mLoadMoreView = loadMoreView;
        addComponent(mLoadMoreView);
        resetLoadMoreState();
        ((ILoadMoreView) mLoadMoreView).reset();


        ((ILoadMoreView) mLoadMoreView).getCanClickFailView().setClickedListener(new ClickedListener() {
            @Override
            public void onClick(Component component) {
                if (isLoadingFail && easyEvent != null) {
                    isLoading = true;
                    ((ILoadMoreView) mLoadMoreView).loading();
                    easyEvent.onLoadMore();
                }
            }
        });

    }

    public void loadMoreComplete() {
        if (loadMoreModel == LoadModel.ADVANCE_MODEL) {
            isLoading = false;
        } else if (loadMoreModel == LoadModel.COMMON_MODEL) {
            loadMoreComplete(null);
        }
    }

    @Deprecated
    public void loadMoreComplete(Event event) {
        if (loadMoreModel == LoadModel.ADVANCE_MODEL) {
            throw new RuntimeException("enableAdance Model cant not called closeLoadView method");

        } else {
            loadMoreComplete(event, 500);
        }
    }

    @Deprecated
    public void loadMoreComplete(final Event event, long delayedTime) {
        if (loadMoreModel == LoadModel.ADVANCE_MODEL) {
            throw new RuntimeException("enableAdance Model cant not called closeLoadView method");

        } else {

            ((ILoadMoreView) mLoadMoreView).loadComplete();

            if (event == null) {
                hideLoadView();
                resetLoadMoreState();
                return;
            }
            postDelayed(() -> {
                event.complete();
                hideLoadView();
                resetLoadMoreState();
            }, delayedTime);
        }
    }

    private void resetLoadMoreState() {
        isCanLoad = false;
        isLoading = false;
        isLoadingFail = false;
        isNotMoreLoading = false;
    }


    public void loadMoreFail() {
        if (loadMoreModel == LoadModel.ADVANCE_MODEL) {
            throw new RuntimeException("enableAdance Model cant not called closeLoadView method");

        } else {
            ((ILoadMoreView) mLoadMoreView).loadFail();
            resetLoadMoreState();
            isLoadingFail = true;
        }
    }

    public void loadNothing() {
        if (loadMoreModel == LoadModel.ADVANCE_MODEL) {
            throw new RuntimeException("enableAdance Model cant not called closeLoadView method");

        } else {
            ((ILoadMoreView) mLoadMoreView).loadNothing();
            resetLoadMoreState();
            isNotMoreLoading = true;
        }
    }

    private Component getDefaultLoadMoreView() {
        SimpleLoadMoreView loadMoreView = new SimpleLoadMoreView(getContext());
        DependentLayout.LayoutConfig layoutConfig = new DependentLayout.LayoutConfig(
                LayoutConfig.MATCH_PARENT,
                LayoutConfig.MATCH_CONTENT);
        loadMoreView.setLayoutConfig(layoutConfig);
        return loadMoreView;
    }

    /**
     * 最后一个的位置
     *
     * @return position
     */
    private int getLastVisiBleItem() {
        int lastVisibleItemPosition = -1;
        int visibleIndexCount = mRecyclerView.getVisibleIndexCount();
        lastVisibleItemPosition = mRecyclerView.getItemPosByVisibleIndex(visibleIndexCount - 1);
        ELog.i(TAG, "lastVisibleItemPosition_" + lastVisibleItemPosition);
        return lastVisibleItemPosition;
    }


    /**
     * 加载更多是否可用
     *
     * @return boolean
     */
    public boolean isEnableLoadMore() {
        return loadMoreModel == LoadModel.COMMON_MODEL || loadMoreModel == LoadModel.ADVANCE_MODEL;
    }

    /**
     * 加载更多模式
     * {@link LoadModel#NONE} 表示不开启
     * {@link LoadModel#COMMON_MODEL} 表示开启普通加载
     * {@link LoadModel#ADVANCE_MODEL} 表示开启预加载模式
     *
     * @return {@link LoadModel}
     */
    public LoadModel getLoadMoreModel() {
        return loadMoreModel;
    }


    /**
     * 设置加载模式
     *
     * @param loadMoreModel 加载更多模式({@link LoadModel)
     * @param advanceCount  预加载数值
     */
    public void setLoadMoreModel(LoadModel loadMoreModel, int advanceCount) {
        this.loadMoreModel = loadMoreModel;
        this.advanceCount = advanceCount;
    }


    /**
     * 获取预加载数值
     *
     * @return 预加载数值
     */
    public int getAdvanceCount() {
        return advanceCount;
    }


    /**
     * 设置预加载数字
     *
     * @param advanceCount 预加载数值
     */
    public void setAdvanceCount(int advanceCount) {
        this.advanceCount = advanceCount;
    }

    /**
     * 默认预加载触发数量为0
     *
     * @param loadMoreModel 加载更多模式({@link LoadModel)
     */
    public void setLoadMoreModel(LoadModel loadMoreModel) {
        this.setLoadMoreModel(loadMoreModel, 0);
    }

    /**
     * 是否正在加载中
     *
     * @return boolean
     */
    public boolean isLoading() {
        return isLoading;
    }

    public interface LoadMoreEvent {
        /***
         * 加载更多
         */
        void onLoadMore();
    }

    public interface Event {
        /**
         * 完成
         */
        void complete();
    }

    /**
     * 获取显示加载更多动画持续时间
     *
     * @return 持续时间
     */
    public long getShowLoadViewAnimatorDuration() {
        return SCROLL_TO_LOADING_DURATION;
    }


    /**
     * 设置显示加载更多动画持续时间
     *
     * @param duration 持续时间
     */
    public void setShowLoadViewAnimatorDuration(long duration) {
        SCROLL_TO_LOADING_DURATION = duration;
    }

    public int getScrollToRefreshDuration() {
        return SCROLL_TO_REFRESH_DURATION;
    }

    public void setScrollToRefreshDuration(int scrollToRefreshDuration) {
        SCROLL_TO_REFRESH_DURATION = scrollToRefreshDuration;
    }

    public int getScrollToTopDuration() {
        return SCROLL_TO_TOP_DURATION;
    }

    public void setScrollToTopDuration(int scrollToTopDuration) {
        SCROLL_TO_TOP_DURATION = scrollToTopDuration;
    }

    public long getHideLoadViewAnimatorDuration() {
        return SHOW_COMPLETED_TIME;
    }

    public void setHideLoadViewAnimatorDuration(long showCompletedTime) {
        SHOW_COMPLETED_TIME = showCompletedTime;
    }


    public double getPullResistance() {
        return this.pull_resistance;
    }

    /**
     * Set the pull-down refresh resistance factor
     *
     * @param PullResistance resistance factor
     */
    public void setPullResistance(double PullResistance) {
        this.pull_resistance = PullResistance;
    }

    @Override
    public void release() {
        easyEvent=null;
        if (eventHandler != null) {
            eventHandler.removeAllEvent();
            eventHandler = null;
        }
        super.release();
    }

}
